package org.mineground;

import java.io.File;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import org.bukkit.WorldCreator;
import org.bukkit.command.CommandExecutor;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
import org.mineground.commands.player.ClassicWorld;
import org.mineground.commands.player.Cmds;
import org.mineground.commands.player.Commands;
import org.mineground.commands.player.CreativeWorld;
import org.mineground.commands.player.Fly;
import org.mineground.commands.player.GroupChat;
import org.mineground.commands.player.Help;
import org.mineground.commands.player.Home;
import org.mineground.commands.player.IRCPM;
import org.mineground.commands.player.LastSeen;
import org.mineground.commands.player.Login;
import org.mineground.commands.player.MainWorld;
import org.mineground.commands.player.Online;
import org.mineground.commands.player.OnlineTime;
import org.mineground.commands.player.PM;
import org.mineground.commands.player.Players;
import org.mineground.commands.player.PvP;
import org.mineground.commands.player.R;
import org.mineground.commands.player.Report;
import org.mineground.commands.player.Rules;
import org.mineground.commands.player.Spawn;
import org.mineground.commands.player.SurvivalWorld;
import org.mineground.commands.player.TP;
import org.mineground.commands.player.Warp;
import org.mineground.commands.player.admin.Ban;
import org.mineground.commands.player.admin.ClearInventory;
import org.mineground.commands.player.admin.Fetch;
import org.mineground.commands.player.admin.ForcePvP;
import org.mineground.commands.player.admin.Give;
import org.mineground.commands.player.admin.God;
import org.mineground.commands.player.admin.Heal;
import org.mineground.commands.player.admin.InvSee;
import org.mineground.commands.player.admin.Kick;
import org.mineground.commands.player.admin.Locate;
import org.mineground.commands.player.admin.MTP;
import org.mineground.commands.player.admin.Mode;
import org.mineground.commands.player.admin.Mute;
import org.mineground.commands.player.admin.Resources;
import org.mineground.commands.player.admin.STP;
import org.mineground.commands.player.admin.Slay;
import org.mineground.commands.player.admin.Time;
import org.mineground.commands.player.admin.Unmute;
import org.mineground.commands.player.admin.Weather;
import org.mineground.handlers.ConfigHandler;
import org.mineground.handlers.MySQLHandler;
import org.mineground.handlers.RandomMessageSender;
import org.mineground.handlers.irc.Handler;
import org.mineground.listener.BlockListener;
import org.mineground.listener.EntityListener;
import org.mineground.listener.PlayerListener;
import org.mineground.listener.ServerListener;
import org.mineground.player.MinegroundPlayer;
import org.mineground.player.ReactionTest;
import org.mineground.player.Supported;
import org.mineground.player.groupchat.GroupChatHandler;
import org.mineground.world.WarpLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.maxmind.geoip.LookupService;

/**
 * @file Main.java (22.01.2012)
 * @author Daniel Koenen
 * 
 */
public class Main extends JavaPlugin {
	private static final Logger ExceptionLogger = LoggerFactory
			.getLogger(Main.class);
	public static final Locale DEFAULT_LOCALE = new Locale("en", "US");
	public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
	public static final String FILE_SEPERATOR = System
			.getProperty("line.separator");

	private static Main instance;

	private Listener playerListener;
	private Listener entityListener;
	private Listener blockListener;
	private Listener serverListener;

	private ConfigHandler configHandler;
	private MySQLHandler databaseHandler;
	private Handler ircHandler;
	private GroupChatHandler groupChatHandler;
	private ReactionTest reactionTestHandler;
	private LookupService geoIPService;
	private LookupService geoIPCityService;
	private RandomMessageSender randomMessageSender;

	private HashMap<Player, MinegroundPlayer> playerTable = new HashMap<Player, MinegroundPlayer>();
	private HashMap<Player, Player> teleportRequests = new HashMap<Player, Player>();

	public long lastTimeTick = 0;

	public Main() {
		instance = this;
	}

	@Override
	public void onEnable() {
		try {
			this.configHandler = new ConfigHandler(this.getConfig());
			this.databaseHandler = new MySQLHandler();
			this.ircHandler = new Handler();
			this.groupChatHandler = new GroupChatHandler();
			this.reactionTestHandler = new ReactionTest();
			this.randomMessageSender = new RandomMessageSender();
			this.geoIPService = new LookupService(new File(Main.getInstance()
					.getDataFolder(), "GeoIP.dat"));

			loadWorlds();
			WarpLoader.load();
			Supported.initializeSupportedKeys();

			this.playerListener = new PlayerListener();
			this.entityListener = new EntityListener();
			this.blockListener = new BlockListener();
			this.serverListener = new ServerListener();

			registerEvents();
			registerCommands();
			registerIRCCommands();

			for (Player player : getServer().getOnlinePlayers()) {
				MinegroundPlayer playerInstance = new MinegroundPlayer(player);
				playerInstance.initializePlayer();
				playerInstance.loadPlayer();
				addPlayer(player, playerInstance);
			}
		}

		catch (Exception exception) {
			ExceptionLogger.error("Exception caught", exception);
		}
	}

	@Override
	public void onDisable() {
		for (Map.Entry<Player, MinegroundPlayer> entry : playerTable.entrySet()) {
			MinegroundPlayer playerInstance = entry.getValue();

			if (playerInstance != null && playerInstance.isRegistered()
					&& playerInstance.isLoggedIn()) {
				entry.getValue().savePlayer();
			}
		}

		try {
			getDatabaseHandler().getConnection().close();
			this.playerTable.clear();
			getReactionTestHandler().killTimer();
			getIRCHandler().quitServer("Visit www.mineground.com");
			getGeoIPService().close();
		}

		catch (Exception exception) {
			ExceptionLogger.error("Exception caught", exception);
		}
	}
	
	public MinegroundPlayer getPlayer(Player player) {
		return this.playerTable.get(player);
	}

	public void addPlayer(Player player, MinegroundPlayer instance) {
		this.playerTable.put(player, instance);
	}

	public void removePlayer(Player player) {
		this.playerTable.remove(player);
	}

	public boolean containsPlayer(Player player) {
		return this.playerTable.containsKey(player);
	}

	public void addTeleportRequest(Player player, Player recv) {
		this.teleportRequests.put(player, recv);
	}

	public void removeTeleportRequest(Player player) {
		this.teleportRequests.remove(player);
	}

	public boolean containsTeleport(Player player) {
		return this.playerTable.containsKey(player);
	}

	public Player getTeleportRequest(Player player) {
		return this.teleportRequests.get(player);
	}

	private void registerCommands() {
		Map<String, CommandExecutor> commands = new HashMap<String, CommandExecutor>();

		commands.put("rules", new Rules());
		commands.put("help", new Help());
		commands.put("commands", new Commands());
		commands.put("cmds", new Cmds());
		commands.put("pm", new PM());
		commands.put("r", new R());
		commands.put("pvp", new PvP());
		commands.put("ircpm", new IRCPM());
		commands.put("tp", new TP());
		commands.put("onlinetime", new OnlineTime());
		commands.put("warp", new Warp());
		commands.put("lastseen", new LastSeen());
		commands.put("online", new Online());
		commands.put("players", new Players());
		commands.put("groupchat", new GroupChat());
		commands.put("main", new MainWorld());
		commands.put("survival", new SurvivalWorld());
		commands.put("creative", new CreativeWorld());
		commands.put("classic", new ClassicWorld());
		commands.put("spawn", new Spawn());
		commands.put("home", new Home());
		commands.put("login", new Login());
		commands.put("report", new Report());
		commands.put("fly", new Fly());

		commands.put("slay", new Slay());
		commands.put("time", new Time());
		commands.put("heal", new Heal());
		commands.put("god", new God());
		commands.put("forcepvp", new ForcePvP());
		commands.put("fetch", new Fetch());
		commands.put("weather", new Weather());
		commands.put("mtp", new MTP());
		commands.put("stp", new STP());
		commands.put("mode", new Mode());
		commands.put("resources", new Resources());
		commands.put("ban", new Ban());
		commands.put("kick", new Kick());
		commands.put("locate", new Locate());
		commands.put("mute", new Mute());
		commands.put("unmute", new Unmute());
		commands.put("i", new Give());
		commands.put("invsee", new InvSee());
		commands.put("clearinventory", new ClearInventory());

		for (Map.Entry<String, CommandExecutor> entry : commands.entrySet()) {
			getCommand(entry.getKey()).setExecutor(entry.getValue());
		}
	}

	private void registerIRCCommands() {
		Map<String, org.mineground.handlers.irc.CommandExecutor> ircCommands = new HashMap<String, org.mineground.handlers.irc.CommandExecutor>();

		// Don't use the wrong CommandExecutor class for this.
		ircCommands.put("players", new org.mineground.commands.irc.Players());
		ircCommands.put("addnote", new org.mineground.commands.irc.AddNote());
		ircCommands.put("admin", new org.mineground.commands.irc.Admin());
		ircCommands.put("ban", new org.mineground.commands.irc.Ban());
		ircCommands.put("give", new org.mineground.commands.irc.Give());
		ircCommands.put("ip", new org.mineground.commands.irc.IP());
		ircCommands.put("kick", new org.mineground.commands.irc.Kick());
		ircCommands.put("msg", new org.mineground.commands.irc.MSG());
		ircCommands.put("pm", new org.mineground.commands.irc.PM());
		ircCommands.put("reload", new org.mineground.commands.irc.Reload());
		ircCommands.put("say", new org.mineground.commands.irc.Say());
		ircCommands.put("server", new org.mineground.commands.irc.Server());
		ircCommands.put("time", new org.mineground.commands.irc.Time());
		ircCommands.put("unban", new org.mineground.commands.irc.Unban());
		ircCommands.put("weather", new org.mineground.commands.irc.Weather());
		ircCommands.put("why", new org.mineground.commands.irc.Why());
		ircCommands.put("online", new org.mineground.commands.irc.Online());
		ircCommands.put("session", new org.mineground.commands.irc.Session());
		ircCommands.put("ses", new org.mineground.commands.irc.Session());
		ircCommands.put("nplayers", new org.mineground.commands.irc.NPlayers());
		ircCommands.put("ntotalplayers",
				new org.mineground.commands.irc.NTotalPlayers());
		ircCommands.put("commands", new org.mineground.commands.irc.Commands());
		ircCommands.put("resources",
				new org.mineground.commands.irc.Resources());
		ircCommands.put("raw", new org.mineground.commands.irc.Raw());
		ircCommands.put("save-all", new org.mineground.commands.irc.SaveAll());
		ircCommands.put("setlevel", new org.mineground.commands.irc.SetLevel());
		ircCommands.put("lastbanned",
				new org.mineground.commands.irc.LastBanned());
		ircCommands.put("isbanned", new org.mineground.commands.irc.IsBanned());
		ircCommands.put("mute", new org.mineground.commands.irc.Mute());
		ircCommands.put("unmute", new org.mineground.commands.irc.Unmute());
		ircCommands.put("banip", new org.mineground.commands.irc.BanIP());
		ircCommands.put("ipinfo", new org.mineground.commands.irc.IPInfo());
		ircCommands.put("unbanip", new org.mineground.commands.irc.UnbanIP());
		ircCommands.put("rangeexception",
				new org.mineground.commands.irc.RangeException());
		ircCommands.put("restart", new org.mineground.commands.irc.Restart());
		ircCommands.put("iplocation",
				new org.mineground.commands.irc.IPLocation());
		ircCommands.put("changeplayerpassword",
				new org.mineground.commands.irc.ChangePlayerPassword());
		ircCommands.put("plugin",
				new org.mineground.commands.irc.HandlePlugin());
		ircCommands.put("value", new org.mineground.commands.irc.Value());
		ircCommands.put("reloadmessages",
				new org.mineground.commands.irc.ReloadMessages());

		// Private commands
		ircCommands.put("changepassword",
				new org.mineground.commands.irc.query.ChangePassword());

		for (Map.Entry<String, org.mineground.handlers.irc.CommandExecutor> entry : ircCommands
				.entrySet()) {
			getIRCHandler().commandHandler.addCommand(entry.getKey(),
					entry.getValue());
		}
	}

	private void registerEvents() {
		getServer().getPluginManager()
				.registerEvents(getPlayerListener(), this);
		getServer().getPluginManager()
				.registerEvents(getEntityListener(), this);
		getServer().getPluginManager().registerEvents(getBlockListener(), this);
		getServer().getPluginManager()
				.registerEvents(getServerListener(), this);
	}

	private void loadWorlds() {
		WorldCreator worldCreator = new WorldCreator(getConfigHandler()
				.getClassicWorldName());
		getServer().createWorld(worldCreator);
	}

	public Listener getPlayerListener() {
		return this.playerListener;
	}

	public Listener getEntityListener() {
		return this.entityListener;
	}

	public Listener getBlockListener() {
		return this.blockListener;
	}

	public Listener getServerListener() {
		return this.serverListener;
	}

	public ConfigHandler getConfigHandler() {
		return this.configHandler;
	}

	public MySQLHandler getDatabaseHandler() {
		return this.databaseHandler;
	}

	public Handler getIRCHandler() {
		return this.ircHandler;
	}

	public GroupChatHandler getGroupChatHandler() {
		return this.groupChatHandler;
	}

	public ReactionTest getReactionTestHandler() {
		return this.reactionTestHandler;
	}

	public LookupService getGeoIPService() {
		return this.geoIPService;
	}

	public LookupService getGeoIPCityService() {
		return this.geoIPCityService;
	}

	public RandomMessageSender getRandomMessageSender() {
		return this.randomMessageSender;
	}

	public static Main getInstance() {
		return instance;
	}
}
